import { SkyboxIndices, SkyboxPosition } from "../../geom/SkyboxGeometry";
import { Matrix4 } from "../../core/math/Matrix4";
import { Vector3 } from "../../core/math/Vector3";
import { mat3, mat4 } from "../../core/math/wgpu-matrix.module";
const generate_skybox_vertex_shader = () => {
  return `
  @group(0)  @binding(0) var<uniform> uPMatrix : mat4x4<f32>;
  @group(0)  @binding(1)  var<uniform> view : mat4x4<f32>;
  struct VertexOutput {
      @builtin(position) Position : vec4<f32>,
      @location(0) localPos : vec3<f32>,
  };
  @vertex
  fn main( @location(0) position : vec3<f32>) -> VertexOutput {
      var output : VertexOutput;
      output.localPos=position;
      output.Position = uPMatrix * view *  vec4<f32>(position, 1.0);
      return output;
  }
  `
}
const integrate_brdf_vertex_shader = () => {
  return `
  struct VertexInput{
    @location(0) position : vec3<f32>,
    @location(1) aTexCoords : vec2<f32>,
  }
  struct VertexOutput {
    @builtin(position) Position : vec4<f32>,
    @location(0) TexCoords : vec2<f32>,
  };
  @vertex
  fn main( input:VertexInput) -> VertexOutput {
      var output : VertexOutput;
      output.TexCoords =input.aTexCoords;
      output.Position = vec4(input.position, 1.0);;
      return output;
  }
  `
}
const skybox_fragment_shader = () => {
  return `
  const M_PI =3.1415926535897932384626433832795;
  @group(1) @binding(0) var Sampler: sampler;
  @group(1) @binding(1) var environmentMap: texture_2d<f32>;
  @group(1) @binding(2) var<uniform> isHDR: u32;
  const  invAtan:vec2<f32> = vec2(1.0 / (2.0 * M_PI), 1.0 / M_PI);
  fn SampleSphericalMap( v:vec3<f32>)->vec2<f32>
  {
      var uv:vec2<f32> = vec2(atan2(v.z,v.x), asin(v.y));
      uv *= invAtan;
      uv += 0.5;
      return uv;
  }
  
  @fragment
  fn main(@location(0) localPos : vec3<f32>) -> @location(0) vec4<f32> 
  {       
       var uv:vec2<f32> = SampleSphericalMap(normalize(localPos)); // make sure to normalize localPos
       var tempColor:vec3<f32> = vec3<f32>();
     
       if(isHDR == 1){
        tempColor= textureSample(environmentMap,Sampler,uv).rgb;
       }else{
        tempColor=pow(textureSample(environmentMap,Sampler,uv).rgb, vec3(2.2));
       }
       return vec4(tempColor, 1.0);
  }
  `
}
const generate_irradiance_map_fragment_shader = (intensity=1.0) => {
   
 return `
 @group(1) @binding(0) var Sampler: sampler;
 @group(1) @binding(1) var environmentMap: texture_cube<f32>;
 const  PI:f32= 3.14159265359;
@fragment
fn main(@location(0) localPos : vec3<f32>) -> @location(0) vec4<f32> 
{
    //样本方向等于半球的方向
  let normal:vec3<f32> = normalize(localPos);
  var irradiance:vec3<f32>= vec3<f32>(0.0);  

// 世界坐标系
var up:vec3<f32>=vec3<f32>(0.0, 1.0, 0.0)  ;
let right:vec3<f32> = normalize(cross(up, normal));
up = normalize(cross(normal, right));
var sampleDelta:f32 = 0.025;
var nrSamples:f32 = 0.0;
for(var phi = 0.0; phi <2.0 * PI; phi+= sampleDelta)
{
    for(var theta = 0.0; theta < 0.5 * PI; theta += sampleDelta)
    {
        var tangentSample:vec3<f32> = vec3(sin(theta)*cos(phi),sin(theta)*sin(phi),cos(theta));
        var sampleVec:vec3<f32> = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal;
        irradiance +=textureSample(environmentMap,Sampler, sampleVec).rgb * cos(theta) * sin(theta);
        nrSamples +=1.0 ;
    }
}
irradiance = PI * irradiance * (1.0/f32(nrSamples));
return vec4<f32>(irradiance, 1.0);
 }`
}
const generate_prefilter_map_fragment_shader = (intensity=1.0) => {
  return `
const  PI:f32= 3.14159265359;
@group(1) @binding(0) var Sampler: sampler;
@group(1) @binding(1) var environmentMap: texture_cube<f32>;
@group(1) @binding(2) var<uniform> roughness: f32;
// @group(1) @binding(3) var<uniform> resolution: f32;

fn RadicalInverse_VdC(bits:u32)->f32{
  var bits1:u32=bits;
  bits1 = (bits1 << 16u) | (bits1 >> 16u);
  bits1 = ((bits1 & 0x55555555u) << 1u) | ((bits1 & 0xAAAAAAAAu) >> 1u);
  bits1 = ((bits1 & 0x33333333u) << 2u) | ((bits1 & 0xCCCCCCCCu) >> 2u);
  bits1 = ((bits1 & 0x0F0F0F0Fu) << 4u) | ((bits1 & 0xF0F0F0F0u) >> 4u);
  bits1 = ((bits & 0x00FF00FFu) << 8u) | ((bits1 & 0xFF00FF00u) >> 8u);
   return f32(bits) * 2.3283064365386963e-10; // / 0x100000000
}
fn Hammersley(i:u32,N:u32)->vec2<f32>{
   return vec2<f32>(f32(i)/f32(N), RadicalInverse_VdC(i));
}
fn ImportanceSampleGGX(Xi:vec2<f32>,N:vec3<f32>,roughness:f32)->vec3<f32>{
 var  a:f32 = roughness*roughness;
 var   phi:f32 = 2.0 * PI * Xi.x;
 var   cosTheta:f32 = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
 var  sinTheta:f32 = sqrt(1.0 - cosTheta*cosTheta);
  // 从球坐标到笛卡尔坐标
  var H:vec3<f32>;
  H.x = cos(phi) * sinTheta;
  H.y = sin(phi) * sinTheta;
  H.z = cosTheta;
  // 从切空间向量到世界空间样本向量
  var up:vec3<f32>       ;
  if(abs(N.z) < 0.999){
    up= vec3<f32>(0.0, 0.0, 1.0);
  }else{
    up=vec3<f32>(1.0, 0.0, 0.0);
  }
  var tangent:vec3<f32>   = normalize(cross(up, N));
  var bitangent:vec3<f32> = cross(N, tangent);
  var sampleVec:vec3<f32> = tangent * H.x + bitangent * H.y + N * H.z;
  return normalize(sampleVec);
}
fn DistributionGGX( N:vec3<f32>, H:vec3<f32>,  roughness:f32)->f32
{
    var a = roughness*roughness;
    var a2 = a*a;
    var NdotH = max(dot(N, H), 0.0);
    var NdotH2 = NdotH*NdotH;

    var nom   = a2;
    var denom = (NdotH2 * (a2 - 1.0) + 1.0);
    denom = PI * denom * denom;

    return nom / denom;
}
@fragment
fn main(@location(0) localPos : vec3<f32>) -> @location(0) vec4<f32> {
  var N:vec3<f32> = normalize(localPos);    
  var R = N;
  var V = R;
const  SAMPLE_COUNT:u32 = 1024;
var prefilteredColor = vec3(0.0);   
var resolution = 512.0;
var totalWeight = 0.0;
var rotateX=mat3x3f(vec3f(1, 0, 0 ),vec3f(0, 0, -1 ),vec3f( 0, 1, 0 ));
for(var i = 0u; i < SAMPLE_COUNT; i++)
{
        var Xi = Hammersley(i, SAMPLE_COUNT);
        var H = ImportanceSampleGGX(Xi, N, roughness);
        var L  = normalize(2.0 * dot(V, H) * H - V);
        // var L  = normalize(2.0 * dot(V, H) * H - V);

        var NdotL = max(dot(N, L), 0.0);
        if(NdotL > 0.0)
        {   
            var D   = DistributionGGX(N, H, roughness);
            var NdotH:f32 = max(dot(N, H), 0.0);
            var HdotV:f32 = max(dot(H, V), 0.0);
            
            var pdf = D * NdotH / (4.0 * HdotV) + 0.0001;
           
            var saTexel  = 4.0 * PI / (6.0 * resolution * resolution);
            var saSample = 1.0 / (f32(SAMPLE_COUNT) * pdf + 0.0001);
            var mipLevel:f32   ; 
            if(roughness == 0.0){
              mipLevel=  0.0;
            }else{
              mipLevel=  0.5 * log2(saSample / saTexel);
            }
            prefilteredColor += textureSampleLevel(environmentMap,Sampler, L, mipLevel).rgb * NdotL;
            totalWeight      += NdotL;
        }
 }
prefilteredColor = prefilteredColor / totalWeight;
return vec4<f32>(prefilteredColor, 1.0);
}
`
}
const integrate_brdf_fragment_shader = () => {
  return `
  const  M_PI:f32= 3.141592653589793238462643383279;
  @fragment
  fn main(@location(0) TexCoords : vec2<f32>) -> @location(0) vec4<f32>
  {
      var integratedBRDF:vec2<f32> = IntegrateBRDF(TexCoords.x, TexCoords.y);
      return vec4<f32>(integratedBRDF,0,1.0);
  }
  
  fn  IntegrateBRDF( NdotV:f32, roughness:f32)->vec2<f32>
  {
      var V:vec3<f32>;
      V.x = sqrt(1.0 - NdotV*NdotV);
      V.y = 0.0;
      V.z = NdotV;
  
      var A:f32 = 0.0;
      var B:f32 = 0.0;
  
      var N:vec3<f32> = vec3(0.0, 0.0, 1.0);
  
      const  SAMPLE_COUNT:u32 = 1024;
      for(var i:u32 = 0; i < SAMPLE_COUNT; i++)
      {
          var Xi:vec2<f32> = Hammersley(i, SAMPLE_COUNT);
          var H:vec3<f32>  = ImportanceSampleGGX(Xi, N, roughness);
          var L:vec3<f32>  = normalize(2.0 * dot(V, H) * H - V);
          var NdotL:f32 = max(L.z, 0.0);
          var NdotH:f32 = max(H.z, 0.0);
          var VdotH:f32 = max(dot(V, H), 0.0);
          if(NdotL > 0.0)
          {
              var G:f32 = GeometrySmith(N, V, L, roughness);
              var G_Vis:f32 = (G * VdotH) / (NdotH * NdotV);
              var Fc:f32 = pow(1.0 - VdotH, 5.0);
  
              A += (1.0 - Fc) * G_Vis;
              B += Fc * G_Vis;
          }
      }
      A /= f32(SAMPLE_COUNT);
      B /= f32(SAMPLE_COUNT);
      return vec2(A, B);
  }
  
  fn GeometrySchlickGGX( NdotV:f32, roughness:f32)->f32
  {
      var a:f32 = roughness;
      var k:f32 = (a * a) / 2.0;
  
      var nom:f32   = NdotV;
      var denom:f32 = NdotV * (1.0 - k) + k;
  
      return nom / denom;
  }
  fn GeometrySmith( N:vec3<f32>,  V:vec3<f32>,  L:vec3<f32>,  roughness:f32)->f32
  {
      var NdotV:f32  = max(dot(N, V), 0.0);
      var NdotL:f32  = max(dot(N, L), 0.0);
      var ggx2:f32  = GeometrySchlickGGX(NdotV, roughness);
      var ggx1:f32  = GeometrySchlickGGX(NdotL, roughness);
      return ggx1 * ggx2;
  }
  fn ImportanceSampleGGX( Xi:vec2<f32>,  N:vec3<f32>,  roughness:f32)->vec3<f32>
  {
      var a:f32  = roughness*roughness;
      var phi:f32  = 2.0 * M_PI * Xi.x;
      var cosTheta:f32  = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
      var sinTheta:f32  = sqrt(1.0 - cosTheta*cosTheta);
      var H:vec3<f32>;
      H.x = cos(phi) * sinTheta;
      H.y = sin(phi) * sinTheta;
      H.z = cosTheta;
      var up:vec3<f32>= vec3<f32>();
      if(abs(N.z) < 0.999){
        up= vec3<f32>(0.0, 0.0, 1.0);
      }else{
        up=vec3<f32>(1.0, 0.0, 0.0);
      }
      var tangent:vec3<f32>   = normalize(cross(up, N));
      var bitangent:vec3<f32> = cross(N, tangent);
      
      var sampleVec:vec3<f32> = tangent * H.x + bitangent * H.y + N * H.z;
      return normalize(sampleVec);
  }
  fn RadicalInverse_VdC(bits:u32)->f32{
    var bits1:u32=bits;
    bits1 = (bits1 << 16u) | (bits1 >> 16u);
    bits1 = ((bits1 & 0x55555555u) << 1u) | ((bits1 & 0xAAAAAAAAu) >> 1u);
    bits1 = ((bits1 & 0x33333333u) << 2u) | ((bits1 & 0xCCCCCCCCu) >> 2u);
    bits1 = ((bits1 & 0x0F0F0F0Fu) << 4u) | ((bits1 & 0xF0F0F0F0u) >> 4u);
    bits1 = ((bits1 & 0x00FF00FFu) << 8u) | ((bits1 & 0xFF00FF00u) >> 8u);
    return f32(bits1) * 2.3283064365386963e-10; // / 0x100000000
   }
   fn Hammersley(i:u32,N:u32)->vec2<f32>{
    return vec2<f32>(f32(i)/f32(N), RadicalInverse_VdC(i));
   }

  `
}
var captureDirections = [
  [
    [0.0, 0.0, 0.0],
    [1.0, 0.0, 0.0],
    [0.0, -1.0, 0.0]
  ], //+X
  [
    [0.0, 0.0, 0.0],
    [-1.0, 0.0, 0.0],
    [0.0, -1.0, 0.0]
  ], //-X
  [
    [0.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0]
  ], //+Y
  [
    [0.0, 0.0, 0.0],
    [0.0, -1.0, 0.0],
    [0.0, 0.0, -1.0]
  ], //-Y
  [
    [0.0, 0.0, 0.0],
    [0.0, 0.0, 1.0],
    [0.0, -1.0, 0.0]
  ], //+Z
  [
    [0.0, 0.0, 0.0],
    [0.0, 0.0, -1.0],
    [0.0, -1.0, 0.0]
  ] //-Z
];
const generateIBL=async(cubeMapTexture,device,format,context)=>{
  var captureDirections1 = [
    [
      [0.0, 0.0, 0.0],
      [1.0, 0.0, 0.0],
      [0.0, 1.0, 0.0]
    ], //+X
    [
      [0.0, 0.0, 0.0],
      [-1.0, 0.0, 0.0],
      [0.0, 1.0, 0.0]
    ], //-X
    [
      [0.0, 0.0, 0.0],
      [0.0, 1.0, 0.0],
      [0.0, 0, -1.0]
    ], //+Y
    [
      [0.0, 0.0, 0.0],
      [0.0,-1.0, 0.0],
      [0.0, 0.0,1.0]
    ], //-Y
    [
      [0.0, 0.0, 0.0],
      [0.0, 0.0, 1.0],
      [0.0, 1.0, 0]
    ], //+Z
    [
      [0.0, 0.0, 0.0],
      [0.0, 0, -1.0],
      [0.0, 1.0, 0]
    ] //-Z
  ];
  let pos=new Float32Array([
      // back face
      -1.0, -1.0, -1.0, // bottom-left
      1.0,  1.0, -1.0,  // top-right
      1.0, -1.0, -1.0,   // bottom-right         
      1.0,  1.0, -1.0,   // top-right
     -1.0, -1.0, -1.0,  // bottom-left
     -1.0,  1.0, -1.0,  // top-left
     // front face
     -1.0, -1.0,  1.0,   // bottom-left
      1.0, -1.0,  1.0,  // bottom-right
      1.0,  1.0,  1.0,  // top-right
      1.0,  1.0,  1.0,   // top-right
     -1.0,  1.0,  1.0,   // top-left
     -1.0, -1.0,  1.0,   // bottom-left
     // left face
     -1.0,  1.0,  1.0,  // top-right
     -1.0,  1.0, -1.0,  // top-left
     -1.0, -1.0, -1.0,  // bottom-left
     -1.0, -1.0, -1.0,  // bottom-left
     -1.0, -1.0,  1.0,  // bottom-right
     -1.0,  1.0,  1.0,  // top-right
     // right face
      1.0,  1.0,  1.0,  // top-left
      1.0, -1.0, -1.0,  // bottom-right
      1.0,  1.0, -1.0,   // top-right         
      1.0, -1.0, -1.0,   // bottom-right
      1.0,  1.0,  1.0,  // top-left
      1.0, -1.0,  1.0,   // bottom-left     
     // bottom face
     -1.0, -1.0, -1.0,   // top-right
      1.0, -1.0, -1.0,   // top-left
      1.0, -1.0,  1.0,   // bottom-left
      1.0, -1.0,  1.0,   // bottom-left
     -1.0, -1.0,  1.0,   // bottom-right
     -1.0, -1.0, -1.0,   // top-right
     // top face
     -1.0,  1.0, -1.0,  // top-left
      1.0,  1.0 , 1.0,  // bottom-right
      1.0,  1.0, -1.0,  // top-right     
      1.0,  1.0,  1.0,   // bottom-right
     -1.0,  1.0, -1.0,  // top-left
     -1.0,  1.0,  1.0,    // bottom-left      
  ]);
  // 创建顶点缓冲区
  const positionBuffer = device.createBuffer({
    size: SkyboxPosition.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
  });
  const positionBuffer1 = device.createBuffer({
    size: pos.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
  });
  const indicesBuffer = device.createBuffer({
    size: SkyboxIndices.byteLength,
    usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
  });
  device.queue.writeBuffer(positionBuffer1, 0, pos);
  device.queue.writeBuffer(positionBuffer, 0, SkyboxPosition);
  device.queue.writeBuffer(indicesBuffer, 0, SkyboxIndices);
  let directions = [];
  captureDirections1.map((item,index) => {
    // const m = new Matrix4();
    // m.lookAt(new Vector3(...item[0]), new Vector3(...item[1]), new Vector3(...item[2]))
  //  let m=  mat4.lookAt(item[0],item[1],item[2])
  //  m=mat4.rotateZ(m,-Math.PI)
  //  const roate= new Matrix4().makeRotationX(-Math.PI/2)
  //  const roateY= new Matrix4().makeRotationY(-Math.PI)
    // m.multiply(roate)
    // directions.push(m)
    const m = new Matrix4();
    m.lookAt(new Vector3(...item[0]), new Vector3(...item[1]), new Vector3(...item[2]))
   const roate= new Matrix4().makeRotationX(-Math.PI/2)
   const roateY= new Matrix4().makeRotationY(-Math.PI)
    m.multiply(roate)
    directions.push(new Float32Array(m.elements))
  });
  // 创建一个4x4的MVP矩阵
  const uPMatrix = device.createBuffer({
    label: 'uPMatrix',
    size: 4 * 4 * 4, // 4 x 4 x float32
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });
  const pM = new Float32Array([
    1.0000000000000002, 0, 0, 0, 0, 1.0000000000000002, 0, 0, 0, 0,
    -1.1008403361344539, -1.0084033613445378, 0, 0, -1, 0
  ]);
  device.queue.writeBuffer(
    uPMatrix,
    0,
    pM.buffer,
    pM.byteOffset,
    pM.byteLength
  );
  const viewMatrix = device.createBuffer({
    label: 'vMatrix',
    size: 4 * 4 * 4, // 4 x 4 x float32
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });
  // 为Matrix2创建一个统一的组
  const uniformBufferSize = 4 * 4 * 4; // 4x4 matrix
  const sampler = device.createSampler({
    magFilter: 'linear',
    minFilter: 'linear'
  });
  // 辐照度贴图
  const pipelineIrradiance = await device.createRenderPipelineAsync({
    label: 'pipelineIrradiance',
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: generate_skybox_vertex_shader()
      }),
      entryPoint: 'main',
      buffers: [
        {
          arrayStride: 3 * 4,
          attributes: [
            {
              shaderLocation: 0,
              format: 'float32x3',
              offset: 0
            }
          ]
        }
      ]
    },
    fragment: {
      module: device.createShaderModule({
        code: generate_irradiance_map_fragment_shader()
      }),
      entryPoint: 'main',
      targets: [
        {
          format: format
        }
      ]
    },
    primitive: {
      topology: 'triangle-list',
      cullMode: 'none'
    },
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus'
    },
    // multisample: {
    //   count: 4,
    // }
  });
  const cubeMapIrradiance = device.createTexture({
    dimension: '2d',
    size: [32, 32, 6],
    //  sampleCount: 4,
    format: format,
    // mipLevelCount: 1,
    usage:
      GPUTextureUsage.TEXTURE_BINDING |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.COPY_SRC |
      GPUTextureUsage.RENDER_ATTACHMENT
  });
  const vertexBindGroupIrradiance = device.createBindGroup({
    layout: pipelineIrradiance.getBindGroupLayout(0),
    entries: [
      {
        binding: 0,
        resource: {
          buffer: uPMatrix,
          offset: 0,
          size: uniformBufferSize
        }
      },
      {
        binding: 1,
        resource: {
          buffer: viewMatrix,
          offset: 0,
          size: uniformBufferSize
        }
      }
    ]
  });
  const fragmentBindGroupIrradiance = device.createBindGroup({
    layout: pipelineIrradiance.getBindGroupLayout(1),
    entries: [
      {
        binding: 0,
        resource: sampler
      },
      {
        binding: 1,
        resource: cubeMapTexture.createView({
          dimension: 'cube'
        })
      }
    ]
  });
  // 为renderPass创建depthTexture
  const depthTextureIrradiance = device.createTexture({
    size: [32, 32],
    format: 'depth24plus',
    // sampleCount: 4,
    usage: GPUTextureUsage.RENDER_ATTACHMENT
  });
  const depthViewIrradiance = depthTextureIrradiance.createView();
  const renderTextureIrradiance = device.createTexture({
    size: [32, 32],
    format: format,
    sampleCount: 1,
    usage:
      GPUTextureUsage.RENDER_ATTACHMENT |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.COPY_SRC
  });
  const renderViewIrradiance = renderTextureIrradiance.createView();
  const renderPassDescriptorIrradiance = {
    colorAttachments: [
      {
        view: renderViewIrradiance,
        clearValue: { r: 0, g: 0, b: 0, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store'
      }
    ],
    depthStencilAttachment: {
      view: depthViewIrradiance,
      depthClearValue: 1.0,
      depthLoadOp: 'clear',
      depthStoreOp: 'store'
    }
  };
  // renderPassDescriptorIrradiance.colorAttachments[0].view=context.getCurrentTexture().createView()
  for (var i = 0; i < 6; ++i) {
    const commandEncoder = device.createCommandEncoder();
    const passEncoderIrradiance = commandEncoder.beginRenderPass(
      renderPassDescriptorIrradiance
    );
    passEncoderIrradiance.setPipeline(pipelineIrradiance);

    device.queue.writeBuffer(
      viewMatrix,
      0,
      directions[i].buffer,
      directions[i].byteOffset,
      directions[i].byteLength
    );
    passEncoderIrradiance.setVertexBuffer(0, positionBuffer);
    passEncoderIrradiance.setIndexBuffer(indicesBuffer, 'uint16');
    passEncoderIrradiance.setBindGroup(0, vertexBindGroupIrradiance);
    passEncoderIrradiance.setBindGroup(1, fragmentBindGroupIrradiance);
    // passEncoderIrradiance.draw(36, 1, 0, 0);
    passEncoderIrradiance.drawIndexed(SkyboxIndices.length, 1, 0, 0);
    passEncoderIrradiance.end();
    commandEncoder.copyTextureToTexture(
      {
        texture: renderTextureIrradiance
      },
      {
        texture: cubeMapIrradiance,
        origin: [0, 0, i]
      },
      [32, 32]
    );
    device.queue.submit([commandEncoder.finish()]);
  }
  // // 预过滤贴图
  const pipelinePrefilter = await device.createRenderPipelineAsync({
    label: 'pipelinePrefilter',
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: generate_skybox_vertex_shader()
      }),
      entryPoint: 'main',
      buffers: [
        {
          arrayStride: 3 * 4,
          attributes: [
            {
              shaderLocation: 0,
              format: 'float32x3',
              offset: 0
            }
          ]
        }
      ]
    },
    fragment: {
      module: device.createShaderModule({
        code: generate_prefilter_map_fragment_shader()
      }),
      entryPoint: 'main',
      targets: [
        {
          format: format
        }
      ]
    },
    primitive: {
      topology: 'triangle-list',
      cullMode: 'none'
    },
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus'
    }
  });
  var res = 128;
  const cubeMapPrefilter = device.createTexture({
    dimension: '2d',
    size: [res, res, 6],
    format: format,
    mipLevelCount: 5,
    usage:
      GPUTextureUsage.TEXTURE_BINDING |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.COPY_SRC |
      GPUTextureUsage.RENDER_ATTACHMENT
  });
  const roughnessBuffer = device.createBuffer({
    label: 'roughnessBuffer',
    size: 4, // 4 x 4 x float32
    usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  });
  // const resBuffer = device.createBuffer({
  //   label: 'resBuffer',
  //   size: 4, // 4 x 4 x float32
  //   usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
  // });
  // device.queue.writeBuffer(resBuffer, 0, new Float32Array([1024.0]));
  const vertexBindGroupPrefilter = device.createBindGroup({
    layout: pipelinePrefilter.getBindGroupLayout(0),
    entries: [
      {
        binding: 0,
        resource: {
          buffer: uPMatrix,
          offset: 0,
          size: uniformBufferSize
        }
      },
      {
        binding: 1,
        resource: {
          buffer: viewMatrix,
          offset: 0,
          size: uniformBufferSize
        }
      }
    ]
  });
  const fragmentBindGroupPrefilter = device.createBindGroup({
    layout: pipelinePrefilter.getBindGroupLayout(1),
    entries: [
      {
        binding: 0,
        resource: sampler
      },
      {
        binding: 1,
        resource: cubeMapTexture.createView({
          dimension: 'cube'
        })
      },
      {
        binding: 2,
        resource: {
          buffer: roughnessBuffer,
          offset: 0,
          size: 4
        }
      },
      // {
      //   binding: 3,
      //   resource: {
      //     buffer: resBuffer,
      //     offset: 0,
      //     size: 4
      //   }
      // }
    ]
  });
  var maxMipLevels = 5;
  for (var mip = 0; mip < maxMipLevels; ++mip) {
    var mipRes = res * Math.pow(0.5, mip);

    var roughness = mip / (maxMipLevels - 1);
    device.queue.writeBuffer(
      roughnessBuffer,
      0,
      new Float32Array([roughness])
    );
    const depthTexturePrefilter = device.createTexture({
      size: [mipRes, mipRes],
      format: 'depth24plus',
      usage: GPUTextureUsage.RENDER_ATTACHMENT
    });
    const depthViewPrefilter = depthTexturePrefilter.createView();
    const renderTexturePrefilter = device.createTexture({
      size: [mipRes, mipRes],
      format: format,
      sampleCount: 1,
      usage:
        GPUTextureUsage.RENDER_ATTACHMENT |
        GPUTextureUsage.COPY_SRC |
        GPUTextureUsage.TEXTURE_BINDING
    });

    const renderPassDescriptorPrefilter = {
      colorAttachments: [
        {
          view: renderTexturePrefilter.createView(),
          clearValue: { r: 0, g: 0, b: 0, a: 1.0 },
          loadOp: 'clear',
          storeOp: 'store'
        }
      ],
      depthStencilAttachment: {
        view: depthViewPrefilter,
        depthClearValue: 1.0,
        depthLoadOp: 'clear',
        depthStoreOp: 'store'
      }
    };

    for (var i = 0; i < 6; ++i) {
      const commandEncoder = device.createCommandEncoder({});
      const passEncoderPrefilter = commandEncoder.beginRenderPass(
        renderPassDescriptorPrefilter
      );
      passEncoderPrefilter.setPipeline(pipelinePrefilter);
      device.queue.writeBuffer(viewMatrix, 0, directions[i].buffer);
      passEncoderPrefilter.setVertexBuffer(0, positionBuffer);
      passEncoderPrefilter.setIndexBuffer(indicesBuffer, 'uint16');
      passEncoderPrefilter.setBindGroup(0, vertexBindGroupPrefilter);
      passEncoderPrefilter.setBindGroup(1, fragmentBindGroupPrefilter);
      passEncoderPrefilter.drawIndexed(SkyboxIndices.length, 1, 0, 0);
      // passEncoderPrefilter.draw(36, 1, 0, 0);
      passEncoderPrefilter.end();
      const mipLevelSize = {
        width: mipRes,
        height: mipRes
      };
      commandEncoder.copyTextureToTexture(
        {
          texture: renderTexturePrefilter,
          mipLevel: 0
        },
        {
          texture: cubeMapPrefilter,
          origin: [0, 0, i],
          mipLevel: mip
        },
        mipLevelSize
      );

      device.queue.submit([commandEncoder.finish()]);
    }
  }
  // BrdfLut
  // var img = new Image();
  // img.src = lutBase;
  // await img.decode();
  // let imageLut = await createImageBitmap(img);
  // const renderTextureLut = engine.device.createTexture({
  //   dimension: '2d',
  //   size: [imageLut.width, imageLut.height],
  //   format: format,
  //   usage:
  //     GPUTextureUsage.TEXTURE_BINDING |
  //     GPUTextureUsage.COPY_DST |
  //     GPUTextureUsage.RENDER_ATTACHMENT
  // });
  // engine.device.queue.copyExternalImageToTexture(
  //   { source: imageLut },
  //   { texture: renderTextureLut },
  //   [imageLut.width, imageLut.height]
  // );
  let lutPosition = new Float32Array([-1, 1, 0, -1, -1, 0, 1, 1, 0, 1, -1, 0])
  let lutIndices = new Uint32Array([2, 0, 1, 2, 1, 3])
  let lutUv = new Float32Array([0, 1, 0, 0, 1, 1, 1, 0])
  // 创建顶点缓冲区
  const lutpositionBuffer = device.createBuffer({
    size: lutPosition.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
  });
  const lutindicesBuffer = device.createBuffer({
    size: lutIndices.byteLength,
    usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
  });
  const lutuvBuffer = device.createBuffer({
    size: lutUv.byteLength,
    usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
  });
  device.queue.writeBuffer(lutpositionBuffer, 0, lutPosition);
  device.queue.writeBuffer(lutindicesBuffer, 0, lutIndices);
  device.queue.writeBuffer(lutuvBuffer, 0, lutUv);

  const pipelinelut = await device.createRenderPipelineAsync({
    label: 'pipelinelut',
    layout: 'auto',
    vertex: {
      module: device.createShaderModule({
        code: integrate_brdf_vertex_shader()
      }),
      entryPoint: 'main',
      buffers: [
        {
          arrayStride: 3 * 4,
          attributes: [
            {
              shaderLocation: 0,
              format: 'float32x3',
              offset: 0
            }
          ]
        },
        {
          arrayStride: 2 * 4,
          attributes: [
            {
              shaderLocation: 1,
              format: 'float32x2',
              offset: 0
            }
          ]
        }
      ]
    },
    fragment: {
      module: device.createShaderModule({
        code: integrate_brdf_fragment_shader()
      }),
      entryPoint: 'main',
      targets: [
        {
          format: format
        }
      ]
    },
    primitive: {
      topology: 'triangle-list',
      cullMode: 'none'
    },
    depthStencil: {
      depthWriteEnabled: true,
      depthCompare: 'less',
      format: 'depth24plus'
    }
  });
  var resLut = 512;
  const depthTextureLut = device.createTexture({
    size: [resLut, resLut],
    format: 'depth24plus',
    usage: GPUTextureUsage.RENDER_ATTACHMENT
  });
  const renderTextureLut = device.createTexture({
    size: [resLut, resLut],
    format: format,
    sampleCount: 1,
    usage:
      GPUTextureUsage.RENDER_ATTACHMENT |
      GPUTextureUsage.COPY_SRC |
      GPUTextureUsage.TEXTURE_BINDING
  });
  const renderPassDescriptorlut = {
    colorAttachments: [
      {
        view: renderTextureLut.createView(),
        clearValue: { r: 0, g: 0, b: 0, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store'
      }
    ],
    depthStencilAttachment: {
      view: depthTextureLut.createView(),
      depthClearValue: 1.0,
      depthLoadOp: 'clear',
      depthStoreOp: 'store'
    }
  };
  // renderPassDescriptorlut.colorAttachments[0].view=context.getCurrentTexture().createView()
  const commandEncoder = device.createCommandEncoder({});
  const passEncoderlut = commandEncoder.beginRenderPass(
    renderPassDescriptorlut
  );
  passEncoderlut.setPipeline(pipelinelut);
  passEncoderlut.setVertexBuffer(0, lutpositionBuffer);
  passEncoderlut.setVertexBuffer(1, lutuvBuffer);
  passEncoderlut.setIndexBuffer(indicesBuffer, 'uint16');
  passEncoderlut.drawIndexed(lutIndices.length, 1, 0, 0);
  passEncoderlut.end();
  device.queue.submit([commandEncoder.finish()]);
  return{cubeMapIrradiance,cubeMapPrefilter,renderTextureLut}
}
export { generate_skybox_vertex_shader, integrate_brdf_vertex_shader, skybox_fragment_shader, generate_irradiance_map_fragment_shader, generate_prefilter_map_fragment_shader,integrate_brdf_fragment_shader,generateIBL, captureDirections }